package service

import (
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"github.com/spf13/viper"
	"github.com/tycme/gin-chat/helper"
	"github.com/tycme/gin-chat/models"
	"log"
	"net"
	"net/http"
	"strconv"
	"sync"
	"time"
)

var count = 0
var SessionKey = "sessionKey."

func WebsocketMessage(c *gin.Context) {
	uc := c.MustGet("user_claims").(*helper.UserClaims)

	conn, err := (&websocket.Upgrader{
		CheckOrigin: func(r *http.Request) bool { // CheckOrigin解决跨域问题
			return true
		}}).Upgrade(c.Writer, c.Request, nil) // 升级成ws协议
	if err != nil {
		http.NotFound(c.Writer, c.Request)
		return
	}
	if err != nil {
		c.JSON(http.StatusOK, gin.H{
			"code": -1,
			"msg":  "系统异常， upgrade websocket失败, err: " + err.Error(),
		})
		return
	}
	userBasic, err := models.GetUserBasicByIdentity(uc.Identity)
	if err != nil {
		token := c.GetHeader("token")
		if token == "" {
			token, _ = c.GetQuery("token")
		}
		replyMsg := ReplyMsg{

			Content:    " 关闭websocket通讯,系统没有找到登录用户,token=" + token,
			Code:       30012,
			CreateTime: time.Now().UnixMicro(),
		}
		msg, _ := json.Marshal(replyMsg)
		conn.ReadMessage()
		conn.WriteMessage(websocket.TextMessage, []byte(msg))
		conn.Close()
		return
	}

	IncrmentReport()

	client := &Client{
		UserId:    userBasic.UserId,
		Account:   userBasic.Account,
		Socket:    conn,
		Lock:      sync.RWMutex{},
		Send:      make(chan []byte),
		LoginTime: time.Now().Unix(),
	}
	//Manager.Clients[userBasic.Identity] = client
	models.RDB.SAdd(c, SessionKey, userBasic.UserId)
	// 用户注册到用户管理上
	//mutex.Lock()
	Manager.Register <- client

	go client.Read()
	//go client.Write()
	//mutex.Unlock()
	//sendAndWrite(conn, uc)
}

// 消息
type Message struct {
	UserId     string `json:"user_id"`     //发送者
	RoomId     string `json:"room_id"`     //接受者
	Type       int    `json:"type"`        //发送类型  1私聊  2群聊  3心跳
	Media      int    `json:"media"`       //消息类型  1文字 2表情包 3语音 4图片 /表情包
	Content    string `json:"content"`     //消息内容
	CreateTime uint64 `json:"create_time"` //创建时间
	Pic        string `json:"pic"`
	Url        string `json:"url"`
	Desc       string `json:"desc"`
	Amount     int    `json:"amount"` //其他数字统计
}

// 回复的消息
type ReplyMsg struct {
	Type       int    `json:"type"`
	UserId     string `json:"userId"` //发送者
	RoomId     string `json:"roomId"` //接受者
	Media      int    `json:"media"`  //消息类型  1文字 2表情包 3语音 4图片 /表情包
	Code       int    `json:"code"`
	Content    string `json:"content"`
	CreateTime int64  `json:"create_time"` //创建时间
}

// 用户类
type Client struct {
	UserId        string
	MsgTime       time.Time
	Account       string
	Ip            string
	LoginTime     int64
	HeartbeatTime uint64 //心跳时间
	Socket        *websocket.Conn
	Lock          sync.RWMutex
	Send          chan []byte
}

// 广播类，包括广播内容和源用户
type Broadcast struct {
	Client  *Client
	Message []byte
	Type    int
}

// 用户管理
type ClientManager struct {
	//	Clients    map[string]*Client
	Clients    sync.Map
	Broadcast  chan *Broadcast
	Reply      chan *Client
	Register   chan *Client
	Unregister chan *Client
	LoginLock  sync.Mutex
}

var Manager = ClientManager{

	Broadcast:  make(chan *Broadcast),
	Register:   make(chan *Client),
	Reply:      make(chan *Client),
	Unregister: make(chan *Client),
}

func (c *Client) Read() {
	defer func() { // 避免忘记关闭，所以要加上close

		if r := recover(); r != nil {
			log.Println("系统异常，UserId="+c.UserId+" ,Account="+c.Account, "有运行异常，", r)

		}
		Manager.Unregister <- c
		_ = c.Socket.Close()
	}()
	for {
		c.Socket.PongHandler()
		message := new(Message)

		// _,msg,_:=c.Socket.ReadMessage()
		log.Println("等待读取json，发送用户UserId=" + c.UserId + " ,Account=" + c.Account)

		err := c.Socket.ReadJSON(&message) // 读取json格式，如果不是json格式，会报错

		message.UserId = c.UserId
		if err != nil {
			log.Println("读取readjson错误,发送UserId="+message.UserId+",roomId="+message.RoomId+",type="+strconv.Itoa(message.Type)+",数据格式不正确,错误原因", err)

			Manager.Unregister <- c
			c.Socket.Close()

			return

		}
		log.Println("解析json之后,UserId=" + message.UserId + ",roomId=" + message.RoomId + ",type=" + strconv.Itoa(message.Type) + ",Account=" + c.Account)

		if message.Type == 1 {
			insertMsg(message)

			sendGroupMsg(message)

		} else if message.Type == 2 {
			insertMsg(message)
			replyMsg := ReplyMsg{
				UserId:     message.UserId,
				RoomId:     message.RoomId,
				Type:       message.Type,
				Media:      message.Media,
				Code:       200,
				Content:    message.Content,
				CreateTime: time.Now().UnixMicro(),
			}
			msg, _ := json.Marshal(replyMsg)
			sendToClientByID(message, msg)

		} else if message.Type == 3 { //收到心跳信息
			value, ok := Manager.Clients.Load(c.UserId)
			if ok {
				client := value.(*Client)
				client.HeartbeatTime = uint64(time.Now().Unix())
				if err != nil {
					log.Printf(" 心跳错误， UserId="+c.UserId+" ,Account="+c.Account+"，cc.WriteMessage: %v\n", err)

				}

			}
		} else if message.Type == 4 { //拉取历史消息

			replyMsg := ReplyMsg{
				UserId:     message.UserId,
				RoomId:     message.RoomId,
				Media:      message.Media,
				Type:       3,
				Code:       30001,
				Content:    "下拉消息完毕",
				CreateTime: time.Now().UnixMicro(),
			}
			msg, _ := json.Marshal(replyMsg)
			//_ = c.Socket.WriteMessage(websocket.TextMessage, msg)
			sendToClientByID(message, msg)

		} else {
			log.Printf("message.Type不符合标准 UserId="+c.UserId+" ,Account="+c.Account+"，cc.WriteMessage: %v\n", err)

		}
	}
}

func insertMsg(message *Message) {
	jsonString, _ := json.Marshal(message)
	msgBasic := &models.MsgBasic{
		UserId:    message.UserId,
		RoomId:    message.RoomId,
		Data:      string(jsonString),
		Type:      message.Type,
		Content:   message.Content,
		Media:     message.Media,
		Icon:      message.Pic,
		CreatedAt: time.Now().Unix(),
		UpdateAt:  time.Now().Unix(),
	}
	err := models.InsertOneMsg(msgBasic)
	if err != nil {
		log.Printf("[DB ERROR: %+v]\n", err)
	}
}

func sendGroupMsg(message *Message) bool {
	userRooms, err := models.GetUserRoomByRoomId(message.RoomId, message.UserId)
	if err != nil {
		log.Printf("[DB ERROR]: %+v", err)
		replyMsg := ReplyMsg{
			UserId:     message.UserId,
			RoomId:     message.RoomId,
			Type:       1,
			Media:      message.Media,
			Content:    "没有找到对应的房间，sendMsg.TargetIdentity=" + message.RoomId,
			Code:       3000,
			CreateTime: time.Now().UnixMicro(),
		}
		msg, _ := json.Marshal(replyMsg)
		sendToClientByID(message, msg)
		return true
	}
	var count = 0
	replyMsg := ReplyMsg{
		UserId:     message.UserId,
		RoomId:     message.RoomId,
		Type:       1,
		Media:      message.Media,
		Content:    message.Content,
		Code:       200,
		CreateTime: time.Now().UnixMicro(),
	}
	msg, _ := json.Marshal(replyMsg)
	for _, room := range userRooms {
		if message.UserId == room.UserId {
			continue
		}
		if value, ok := Manager.Clients.Load(room.UserId); ok {
			client := value.(*Client)
			IncrmentFlow(2)
			count = count + 1

			//	log.Printf("room.UserId=" + room.UserId)
			//content := `{"seq":"1566276523281-585638","cmd":"heartbeat","response":{"code":200,"codeMsg":"Success","data":"` + message.Content + `"}}`
			err := client.Socket.WriteMessage(websocket.TextMessage, msg)
			if err != nil {
				log.Printf("cc.WriteMessage: %v\n", err)
				return true
			}
		}
	}
	log.Println("发送消息成功，发送用户UserId="+message.UserId+",roomId="+message.RoomId+",type="+strconv.Itoa(message.Type)+",发送消息内容=", message.Content+",发送人员数量="+strconv.Itoa(count))
	return false
}
func sendToClientByID(message *Message, msg []byte) bool {
	IncrmentFlow(2)
	userRooms, _ := models.GetUserRoomByRoomId(message.RoomId, message.UserId)
	if len(userRooms) == 0 {
		value, ok := Manager.Clients.Load(message.UserId)
		if ok {
			replyMsg := ReplyMsg{
				UserId:     message.UserId,
				RoomId:     message.RoomId,
				Type:       2,
				Media:      message.Media,
				Content:    "没有找到对应用户",
				Code:       30010,
				CreateTime: time.Now().UnixMicro(),
			}
			msg, _ := json.Marshal(replyMsg)
			client := value.(*Client)

			err := client.Socket.WriteMessage(websocket.TextMessage, []byte(msg))
			log.Println("UserId=" + message.UserId + ",roomId=" + message.RoomId + "，type=" + strconv.Itoa(message.Type) + ",Account=" + client.Account + ",在该房间找到对应的用户")
			if err != nil {
				log.Println("cc.WriteMessage: %v\n", err)

			}
			return true

		}
	}
	userRoom := userRooms[0]
	value, ok := Manager.Clients.Load(userRoom.UserId)
	if ok {
		client := value.(*Client)
		err := client.Socket.WriteMessage(websocket.TextMessage, msg)
		if err != nil {
			log.Printf("UserId="+message.UserId+"  roomId"+message.RoomId+" type="+strconv.Itoa(message.Type)+" ,client.Account="+client.Account+"，cc.WriteMessage: %v\n", err)

		}
		return true
	}
	log.Printf("UserId=" + message.UserId + "  roomId=" + message.RoomId + " type=" + strconv.Itoa(message.Type) + ",该房间没有找到对应用户")
	return false
}

func SendToAllClient(c *gin.Context) {
	content, _ := c.GetQuery("content")
	Manager.Clients.Range(func(key, value interface{}) bool {

		client := value.(*Client)
		replyMsg := ReplyMsg{
			UserId:     "1000", //管理员
			Type:       -1,     //群发消息
			Content:    content,
			Code:       200,
			CreateTime: time.Now().UnixMicro(),
		}
		msg, _ := json.Marshal(replyMsg)
		//client.Lock.Lock()
		err := client.Socket.WriteMessage(websocket.TextMessage, msg)
		//client.Lock.Unlock()
		if err != nil {
			log.Printf("UserId="+client.UserId+" ,client.Account="+client.Account+"，cc.WriteMessage: %v\n", err)

		}
		return true
	})
	var count int
	Manager.Clients.Range(func(key, value interface{}) bool {
		count++
		return true
	})
	c.JSON(http.StatusOK, gin.H{
		"code":  200,
		"count": count,
		"msg":   "群发消息完毕 ",
	})

}

func (c *Client) Write() {
	defer func() {
		_ = c.Socket.Close()
	}()
	for {
		select {
		case message, ok := <-c.Send:
			if !ok {
				_ = c.Socket.WriteMessage(websocket.CloseMessage, []byte{})
				return
			}
			//log.Println(c.Identity, "接受消息:", string(message))
			//replyMsg := ReplyMsg{
			//	Code:    555555,
			//	Content: fmt.Sprintf("%s", string(message)),
			//}
			//msg, _ := json.Marshal(replyMsg)
			//	c.Lock.Lock()
			_ = c.Socket.WriteMessage(websocket.TextMessage, message)
			//c.Lock.Unlock()
		}
	}
}
func sendUdp(data []byte) {
	con, err := net.DialUDP("udp", nil, &net.UDPAddr{
		IP:   net.IPv4(192, 168, 1, 255), //广播地址
		Port: 2222,
	})
	if err != nil {
		log.Println(err)
		return
	}
	defer con.Close()

	n, err := con.Write(data)
	if err != nil {
		log.Println(err)
	}
	fmt.Println("udp broadcast msg:", n, "bytes")
}

func StartUdp() {
	fmt.Println("启动udp服务")
	listen, err := net.ListenUDP("udp", &net.UDPAddr{
		IP:   net.IPv4(0, 0, 0, 0), //代表本机所有网卡地址
		Port: 30000,
	})
	if err != nil {
		log.Println(err)
		return
	}
	defer listen.Close()

	for {
		var buf [1024]byte
		n, addr, err := listen.ReadFromUDP(buf[:])
		if err != nil {
			log.Println(err)
			break
		}
		if n > 0 {
			fmt.Println("udp receive msg:", string(buf[:n]), "地址：", addr.IP.String(), ":", addr.Port)
			handleUdpMsg(buf[:n]) //处理消息
		}
	}
}
func handleUdpMsg(msg []byte) {

	var message Message
	err := json.Unmarshal(msg, &message)
	if err != nil {
		log.Println(err)
		return
	}
	if message.Type == 1 {
		sendGroupMsg(&message)
	} else if message.Type == 2 {
		sendToClientByID(&message, msg)
	}

	//log.Println("成功发送消息到用户", data.UserId)
}

func GetSyncMap(m sync.Map) int {
	var count int
	m.Range(func(key, value interface{}) bool {
		count++
		return true
	})
	return count
}

// 清理超时连接
func CleanConnection(param interface{}) (result bool) {
	result = true
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("cleanConnection err", r)
		}
	}()
	//	fmt.Println("timeout.Heartbeat=" + viper.GetString("timeout.Heartbeat"))
	if viper.GetString("timeout.Heartbeat") != "on" {
		return true
	}
	fmt.Println("定时任务,清理超时连接 ", GetSyncMap(Manager.Clients))
	//fmt.Println("定时任务,清理超时连接 ", param)
	//node.IsHeartbeatTimeOut()

	currentTime := uint64(time.Now().Unix())
	Manager.Clients.Range(func(key, value interface{}) bool {
		client := value.(*Client)
		if client.IsHeartbeatTimeOut(currentTime) {
			Manager.Clients.Delete(key)
			fmt.Println("心跳超时..... 关闭连接：,client.UserId=", client.UserId, ",client.account="+client.Account)
			client.Socket.Close()
		}
		return true
	})

	return result
}

// 用户心跳是否超时
func (client *Client) IsHeartbeatTimeOut(currentTime uint64) (timeout bool) {
	if client.HeartbeatTime+viper.GetUint64("timeout.HeartbeatMaxTime") <= currentTime {
		fmt.Println("心跳超时。。。自动下线", client)
		timeout = true
	}
	return
}
